#include <stdint.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <gdsl/gdsl_types.h>
#include <gdsl/gdsl_rbtree.h>

#include "vec.h"

typedef vec_t(gdsl_element_t *) vec_rbtree_node_t;

char* strdup(const char* s) {
        size_t slen = strlen(s);
        char* result = malloc(slen + 1);
        if(result == NULL) {
                return NULL;
        }

        memcpy(result, s, slen+1);
        return result;
}

#define BITMAP_UNIT_STATUS_DIRTY 0x00000001

typedef struct _my_struct {
        uint64_t lba;
        uint32_t chunk_idx;
        uint32_t page_idx;
        int flag;
} my_struct;

typedef struct __map_ctx_t {
        int count;
        uint64_t prev_lba;
        uint64_t max_lba;
        vec_rbtree_node_t v;
} map_ctx_t;

gdsl_element_t my_struct_alloc (void* d)
{
        static int n = 10;

        my_struct *e = (my_struct*)malloc (sizeof (struct _my_struct));
        if (e == NULL) {
                return NULL;
        }

        return (gdsl_element_t) e;
}

void my_struct_free (gdsl_element_t e)
{
        // my_struct *s = (my_struct*)e;
        free (e);
}

static void vec_print(vec_rbtree_node_t *v) {
        int i;
        my_struct *val;

        vec_foreach(v, val, i) {
                // printf("\ti %d lba %llu\n", i, (unsigned long long)val->lba);
        }
        vec_deinit(v);
        // printf("\n");
}

#define N 400000
#define PAGE_SIZE 50

static int infix_map_f (const gdsl_element_t e,
                gdsl_location_t location,
                void* user_data)
{
        int i;
        my_struct *_e = e, *val;
        if (user_data) {
                map_ctx_t *ctx = user_data;
                if (ctx->prev_lba == 0) {
                        ctx->prev_lba = _e->lba;
                        vec_push(&ctx->v, e);
                } else {
                        if (_e->lba / PAGE_SIZE != ctx->prev_lba / PAGE_SIZE) {
                                // printf("break here %llu\n", (unsigned long long)_e->lba);
                                vec_print(&ctx->v);
                        }
                        ctx->prev_lba = _e->lba;
                        vec_push(&ctx->v, e);
                }

                ctx->count ++;
                ctx->max_lba = _e->lba;
        }
        // printf ("\t%llu\n",  (unsigned long long)_e->lba);
        return GDSL_MAP_CONT;
}

long int my_struct_cmp (gdsl_element_t s1, void* s2)
{
        my_struct *e1 = s1;
        my_struct *e2 = s2;

        return e1->lba - e2->lba;
}

void print_string (gdsl_element_t e, FILE* file, gdsl_location_t location, void* d)
{
        char loc [256] = "";

        if (location & GDSL_LOCATION_ROOT) {
                strcat (loc, "ROOT ");
        }

        if (location & GDSL_LOCATION_LEAF) {
                strcat (loc, "LEAF ");
        }

        if (d == NULL) {
                fprintf (file, "%s%s", (char*) e, loc);
        } else {
                fprintf (file, "%s%s%s", (char*) e, loc, (char*) d);
        }
}


int main(int argc, char *argv[]) {
        gdsl_rbtree_t rbtree;

        rbtree = gdsl_rbtree_alloc ("BITMAP", NULL, my_struct_free, my_struct_cmp);
        if (rbtree == NULL) {
                exit (EXIT_FAILURE);
        }

        //gdsl_element_t e;
        srand(gettime());
        my_struct *e;
        int rc;
        for (int i=0; i < N; i++) {
                e = malloc(sizeof(my_struct));
                e->lba = i;
                e->chunk_idx = i;
                e->page_idx = i;
                gdsl_rbtree_insert(rbtree, (void *)e, &rc);
        }

#if 0
        my_struct s;
        s.lba = 4;
        gdsl_element_t *found = gdsl_rbtree_search(rbtree, NULL, (void *)&s);
        printf("found %p\n", found);

        gdsl_rbtree_delete(rbtree, (void *)&s);
        //printf("remove %p\n", found);

        found = gdsl_rbtree_search(rbtree, NULL, (void *)&s);
        printf("found %p\n", found);
        // gdsl_rbtree_dump(rbtree, print_string, stdout, "");
#endif

        map_ctx_t ctx;
        vec_init(&ctx.v);
        ctx.count = 0;
        ctx.prev_lba = 0;

        gdsl_rbtree_map_infix(rbtree, infix_map_f, (void *)&ctx);

        // printf("infix: %ld, count %d\n", gdsl_rbtree_get_size(rbtree), ctx.count);

        vec_print(&ctx.v);

#if 0
        ctx.count = 0;
        ctx.prev_lba = 0;

        printf("infix: %ld\n", gdsl_rbtree_get_size(rbtree));
        gdsl_rbtree_map_infix(rbtree, infix_map_f, (void *)&ctx);
#endif

        while(1) {
                if (gdsl_rbtree_is_empty(rbtree)) {
                        break;
                }
                break;

        }

        gdsl_rbtree_flush(rbtree);
        printf("infix: %ld, count %d, max %llu\n", gdsl_rbtree_get_size(rbtree),
                        ctx.count, (unsigned long long)ctx.max_lba);
        gdsl_rbtree_free(rbtree);

        return 0;
}
